/*
 * Killocan 2013
 * http://killocan.blogspot.com
*/

#include <allegro.h>
#include <stdio.h>

#include "stagemanager.h"
#include "sfx.h"

#include "stage.h"
#include "camera.h"
#include "player.h"

#include "weapons.h"
#include "weaponmenu.h"
#include "energybar.h"

#include "tileactions.h"
#include "defines.h"

#include "globals.h"
#include "globalgamestate.h"

#include "collision.h"

#define FPS_IN_GAME

#ifdef FPS_IN_GAME
volatile int sm_fps       = 0;
volatile int sm_fps_count = 0;
static void sm_check_fps()
{
  sm_fps = sm_fps_count;
  sm_fps_count = 0;
}
END_OF_STATIC_FUNCTION(sm_check_fps);
#endif

StageManager::StageManager()
{
  camera = new Camera;
  GlobalCamera::mm_camera = camera;

  player = NULL;
  stage  = NULL;
  ssm    = NULL;

  game_pause  = false;
  weapon_menu = false;

  Clock::clockTicks = 0;

#ifdef FPS_IN_GAME
  LOCK_VARIABLE(sm_fps);
  LOCK_VARIABLE(sm_fps_count);
  LOCK_FUNCTION(sm_check_fps);
  install_int_ex(sm_check_fps, BPS_TO_TIMER(1));
#endif
}

StageManager::~StageManager()
{
  Character * curr_character = NULL;
  std::vector<Character *>::iterator it;
  for (it = characters_vec.begin(); it != characters_vec.end(); ++it)
  {
    curr_character = *it;
    delete curr_character;
  }
  //characters_vec.clear();

  delete stage;
  //delete player;
  delete camera;
  GlobalCamera::mm_camera = NULL;

  delete weaponMenu;
}

SceneSoundManager * StageManager::CreateSoundManager()
{
  return NULL;
}

const std::string & StageManager::getStageFilePath() const
{
  return STAGE_PATH;
}

void StageManager::doMegamanSpawning(BITMAP * buffer, FONT * mm_font)
{
  bool played_teletransport_sound = false;
  int y_goal = player->y;
  player->y = -player->h;

  int animCount = 0;

  //These are the default values reseting here just to be clear about it.
  player->setAnimSeq(Player::SPAWNING);
  player->curAnimFrame = 0;

  bool goingToCheckpoint = true;
  while(goingToCheckpoint == true && animCount < 4)
  {
    if (GlobalGameState::game_logic_tick == 1)
    {
      player->y+=8;
      if (player->y >= y_goal)
      {
        if (played_teletransport_sound == false)
        {
          played_teletransport_sound = true;
          Sounds::mm_sounds->play(TELE);
          player->onground = true;
        }

        player->y = y_goal;
        
        if (player->curAnimFrameDuration == 0)
        {
          player->nextAnimFrame();
          animCount++;
        }
        else
        {
          player->curAnimFrameDuration--;
        }
      }

      GlobalGameState::game_logic_tick = 0;
    }

    if (stage->has_fg_tiles == false)
    {
      stage->draw(buffer, *camera, false);
      player->calcScreenCoords(*camera);
      player->drawCharacter(buffer);
    }
    else
    {
      stage->draw(buffer, *camera, true, false, true);
      player->calcScreenCoords(*camera);
      player->drawCharacter(buffer);
      stage->draw(m_buffer, *camera, true, false, false);
    }

    //Vsync::Sync();
    blit(buffer,screen,0,0,0,0,SCREEN_W,SCREEN_H);
    clear_bitmap(buffer);
  }
  
  player->y = y_goal;
  //Stand still sequence.
  player->setAnimSeq(Player::STANDSTILL);
}

#if 0
void StageManager::doDraw()
{
  Character * curr_character = NULL;
  std::vector<Character *>::iterator it;

  stage->draw(m_buffer, *camera);
  
  for (it = characters_vec.begin(); it != characters_vec.end(); ++it)
  {
    curr_character = *it;
    curr_character->calcScreenCoords(*camera);
    curr_character->drawCharacter(m_buffer);
  }
}
#endif

/**
 * Check for collisions between player bullets and enemies AND
 * between enemies and the player
 */
void StageManager::checkColissionAgainstEnemy()
{
  Character * curr_character = NULL;
  std::vector<Character *>::iterator it;
  for (it = characters_vec.begin(); it != characters_vec.end(); ++it)
  {
    curr_character = *it;
    if (curr_character == player) continue;

    if (curr_character->alive == true)
    {
      for (std::list<mm_weapons::weapon_st>::iterator it_w = GlobalGameState::playerShots.begin();
           it_w != GlobalGameState::playerShots.end();
           ++it_w)
      {
        mm_weapons::weapon_st * pWeapon = &(*it_w);

        if (Collision::pixelCollision((int)pWeapon->x,    (int)pWeapon->y,   pWeapon->bulletSpriteShet->getFrame(pWeapon->frameOffset),
                                       curr_character->x, curr_character->y, curr_character->getFrame(),
                                       &pWeapon->xcol, &pWeapon->ycol) == true)
        {
          curr_character->hit(pWeapon);
        }
      }

      if (player->isInvincible == false)
      {
        if (Collision::pixelCollision((int)player->x,    (int)player->y,   player->getFrame(),
                                       curr_character->x, curr_character->y, curr_character->getFrame(),
                                       &player->xcol, &player->ycol) == true)
        {
          player->hit(curr_character);
          curr_character->collideWithPlayer = true;
        }
      }
    }
  }
}

void StageManager::checkColissionPlayerEnemyBullets()
{
  if (player->isInvincible == false)
  {
    for (std::list<mm_weapons::weapon_st>::iterator it_w = GlobalGameState::enemyShots.begin();
         it_w != GlobalGameState::enemyShots.end();
         ++it_w)
    {
      mm_weapons::weapon_st * pWeapon = &(*it_w);

      if (Collision::pixelCollision((int)pWeapon->x,    (int)pWeapon->y,   pWeapon->bulletSpriteShet->getFrame(pWeapon->frameOffset),
                                     player->x, player->y, player->getFrame()) == true)
      {
        player->hit(pWeapon);
      }
    }
  }
}

static bool tempCharacterDone(const Character* value) 
{ 
  if (value->alive == false)
  {
    delete value;
    return true;
  }

  return false;
}
void StageManager::play()
{
  stage  = new Stage(getStageFilePath(), *camera, &player);//, characters_vec);
  setupStage(); // Workaround :)
  stage->createEnemies(characters_vec); // Load all enemys.

  ssm        = CreateSoundManager();
  weaponMenu = new WeaponMenu(0, player);
  EnergyBar::m_player = player;

  characters_vec.push_back(player);

  m_buffer         = Buffer::buffer;
  FONT   * mm_font = Font::mm_font;

#ifdef DEBUG
  fprintf(stderr, "Tem tiles foreground = [%d]\n", (int)stage->has_fg_tiles);
#endif

  clear_bitmap(m_buffer);
  stage->doCamera(*player, *camera);
  stage->draw(m_buffer, *camera, stage->has_fg_tiles);
  blit(m_buffer,screen,0,0,0,0,SCREEN_W,SCREEN_H);

  Character * curr_character = NULL;
  std::vector<Character *>::iterator it;

  bool playing   = true;
  bool game_over = false;
  while(game_over == false)
  {
    GlobalGameState::playerShots.clear();
    GlobalGameState::enemyShots.clear();

    Sounds::mm_sounds->stopAll();
    ssm->play(0, true);

    player->reset();
    //TODO: get it from waypoint sector.
    camera->y = 0;
    stage->doCamera(*player, *camera);

#if 0
    textout_centre_ex(screen, mm_font, "READY",
                      SCREEN_W/2, SCREEN_H/2,
                      makecol(255,255,255), -1);
    rest(3000);
#endif

    doMegamanSpawning(m_buffer, mm_font);

    playing = true;
    while(playing == true)
    {
      if (key[KEY_Q]) 
      { 
        playing = !(game_over = true); 
      }

      if (key[KEY_P] && weapon_menu == false)
      {
        if (game_pause == false)
        {
          Sounds::mm_sounds->play(PAUSE);
        }

        game_pause = !game_pause;
        while(key[KEY_P]);
      }
      
      if (key[KEY_ENTER] && game_pause == false)
      {
        Sounds::mm_sounds->play(PAUSE);

        game_pause = true;
        weapon_menu = true;
      }

      if (GlobalGameState::game_logic_tick == 1)
      {
        GlobalGameState::game_logic_tick = 0;

        if (game_pause == false)
        {
          for (it = characters_vec.begin(); it != characters_vec.end(); ++it)
          {
            curr_character = *it;

            curr_character->checkOnCamera(camera);
            if (curr_character->alive == true)
            {
              curr_character->handleAnimation();

              if (stage->horz_scroll == false)
              {
                curr_character->doGravitation();
              }

              curr_character->doLogic();
            }
          }

          for (std::list<Character *>::iterator i = TemporaryCharacterList::mm_tempCharacterLst.begin();
               i != TemporaryCharacterList::mm_tempCharacterLst.end();
               ++i)
          {
            curr_character = *i;
            curr_character->doGravitation();
            curr_character->doLogic();
            if (curr_character->canCollidePlayer == true)
            {
              if (Collision::pixelCollision(curr_character->x, curr_character->y, curr_character->getFrame(),
                                            player->x, player->y, player->getFrame()) == true)
              {
                player->hit(curr_character);
              }
            }
            if (curr_character->canCollideBullet == true)
            {
              for (std::list<mm_weapons::weapon_st>::iterator it_w = GlobalGameState::playerShots.begin();
                   it_w != GlobalGameState::playerShots.end();
                   ++it_w)
              {
                mm_weapons::weapon_st * pWeapon = &(*it_w);
                if (Collision::pixelCollision((int)pWeapon->x,    (int)pWeapon->y,   pWeapon->bulletSpriteShet->getFrame(pWeapon->frameOffset),
                                               curr_character->x, curr_character->y, curr_character->getFrame(),
                                               &curr_character->xcol, &curr_character->ycol) == true)
                {
                  curr_character->hit(pWeapon);
                }
              }
            }
          }
          TemporaryCharacterList::mm_tempCharacterLst.remove_if(tempCharacterDone);

          checkColissionAgainstEnemy();
          checkColissionPlayerEnemyBullets();

          stage->doCamera(*player, *camera);

          mm_weapons::updateWeapons(camera);

          doStageSpecifics();
        }

        ++Clock::clockTicks;
      }

      if (player->alive == false)
      {
        if (ssm->isPlaying() == true) 
        {
          ssm->stopAll();
        }
        if (TemporaryCharacterList::mm_tempCharacterLst.size() == 0)
        {
          playing = false;
        }
      }

      if (player->lives == 0) 
      {
        game_over = false;
      }

      //Most of time there are no FG tiles.
      if (stage->has_fg_tiles == false)
      {
        stage->draw(m_buffer, *camera, false);
        
        for (it = characters_vec.begin(); it != characters_vec.end(); ++it)
        {
          curr_character = *it;

          if (curr_character->alive == true)
          {
            curr_character->calcScreenCoords(*camera);
            curr_character->drawCharacter(m_buffer);
          }
        }
        for (std::list<Character *>::iterator i = TemporaryCharacterList::mm_tempCharacterLst.begin();
             i != TemporaryCharacterList::mm_tempCharacterLst.end();
             ++i)
        {
          curr_character = *i;
          curr_character->calcScreenCoords(*camera);
          curr_character->drawCharacter(m_buffer);
        }
      }
      else if (stage->cameraSectorHasFgTiles(*camera, *player) == true)
      {
        clear_bitmap(m_buffer);
        stage->draw(m_buffer, *camera, true, false, true);
        for (it = characters_vec.begin(); it != characters_vec.end(); ++it)
        {
          curr_character = *it;

          if (curr_character->alive == true)
          {
            curr_character->calcScreenCoords(*camera);
            curr_character->drawCharacter(m_buffer);
          }
        }
        for (std::list<Character *>::iterator i = TemporaryCharacterList::mm_tempCharacterLst.begin();
             i != TemporaryCharacterList::mm_tempCharacterLst.end();
             ++i)
        {
          curr_character = *i;
          curr_character->calcScreenCoords(*camera);
          curr_character->drawCharacter(m_buffer);
        }
        stage->draw(m_buffer, *camera, true, false, false);
      }
      else
      {
        clear_bitmap(m_buffer);
        stage->draw(m_buffer, *camera, true);
        
        for (it = characters_vec.begin(); it != characters_vec.end(); ++it)
        {
          curr_character = *it;

          if (curr_character->alive == true)
          {
            curr_character->calcScreenCoords(*camera);
            curr_character->drawCharacter(m_buffer);
          }
        }
        for (std::list<Character *>::iterator i = TemporaryCharacterList::mm_tempCharacterLst.begin();
             i != TemporaryCharacterList::mm_tempCharacterLst.end();
             ++i)
        {
          curr_character = *i;
          curr_character->calcScreenCoords(*camera);
          curr_character->drawCharacter(m_buffer);
        }
      }

      mm_weapons::drawWeapons(m_buffer);

      if (weapon_menu == true)
      {
        weapon_menu = weaponMenu->imputAndDraw(m_buffer);
        if (weapon_menu == false)
        {
          game_pause = false;
        }
      }

      EnergyBar::drawEnerybar(m_buffer, 48, 34, mm_weapons::W_MEGA_BUSTER);
      if (player->curWeapon != mm_weapons::W_MEGA_BUSTER)
      {
        EnergyBar::drawEnerybar(m_buffer, 32, 34, player->curWeapon);        
      }

#ifdef FPS_IN_GAME
      textprintf_ex(m_buffer, font, 1, 1, makecol(255,255,255), 0, "FPS: [%d]", sm_fps);
#endif
      //Vsync::Sync();
      blit(m_buffer,screen,0,0,0,0,SCREEN_W,SCREEN_H);
#ifdef FPS_IN_GAME
      sm_fps_count++;
#endif
    }
  }

  ssm->stopAll();
  delete ssm;
}

void StageManager::doStageSpecifics()
{
  return;
}

void StageManager::setupStage()
{
  return;
}
